
describe("TextMate Ruby grammar", function() {
  let grammar = null;

  beforeEach(function() {
    atom.config.set('core.useTreeSitterParsers', false);


    waitsForPromise(() => atom.packages.activatePackage("language-ruby"));

    runs(() => grammar = atom.grammars.grammarForScopeName("source.ruby"));
  });

  it("parses the grammar", function() {
    expect(grammar).toBeTruthy();
    expect(grammar.scopeName).toBe("source.ruby");
  });

  it("tokenizes self", function() {
    const {tokens} = grammar.tokenizeLine('self');
    expect(tokens[0]).toEqual({value: 'self', scopes: ['source.ruby', 'variable.language.self.ruby']});
});

  it("tokenizes special functions", function() {
    let {tokens} = grammar.tokenizeLine('require "."');
    expect(tokens[0]).toEqual({value: 'require', scopes: ['source.ruby', 'meta.require.ruby', 'keyword.other.special-method.ruby']});

    ({tokens} = grammar.tokenizeLine('Kernel.require "."'));
    expect(tokens[1]).toEqual({value: '.', scopes: ['source.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[2]).toEqual({value: 'require ', scopes: ['source.ruby']});

    ({tokens} = grammar.tokenizeLine('Kernel::require "."'));
    expect(tokens[1]).toEqual({value: '::', scopes: ['source.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[2]).toEqual({value: 'require ', scopes: ['source.ruby']});

    ({tokens} = grammar.tokenizeLine('thing&.call'));
    expect(tokens[1]).toEqual({value: '&.', scopes: ['source.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[2]).toEqual({value: 'call', scopes: ['source.ruby']});
});

  it("tokenizes variable constants", function() {
    let {tokens} = grammar.tokenizeLine('VAR1 = 100');
    expect(tokens[0]).toEqual({value: 'VAR1', scopes: ['source.ruby', 'variable.other.constant.ruby']});

    ({tokens} = grammar.tokenizeLine('_VAR2 = 200'));
    expect(tokens[0]).toEqual({value: '_VAR2', scopes: ['source.ruby', 'variable.other.constant.ruby']});
});

  it("tokenizes decimal numbers", function() {
    let {tokens} = grammar.tokenizeLine('100_000');
    expect(tokens[0]).toEqual({value: '100_000', scopes: ['source.ruby', 'constant.numeric.ruby']});

    ({tokens} = grammar.tokenizeLine('0'));
    expect(tokens[0]).toEqual({value: '0', scopes: ['source.ruby', 'constant.numeric.ruby']});

    ({tokens} = grammar.tokenizeLine('1.23'));
    expect(tokens[0]).toEqual({value: '1.23', scopes: ['source.ruby', 'constant.numeric.ruby']});

    ({tokens} = grammar.tokenizeLine('1.23e-4'));
    expect(tokens[0]).toEqual({value: '1.23e-4', scopes: ['source.ruby', 'constant.numeric.ruby']});

    ({tokens} = grammar.tokenizeLine('0d100_000'));
    expect(tokens[0]).toEqual({value: '0d100_000', scopes: ['source.ruby', 'constant.numeric.ruby']});
});

  it("tokenizes hexadecimal numbers", function() {
    let {tokens} = grammar.tokenizeLine('0xAFFF');
    expect(tokens[0]).toEqual({value: '0xAFFF', scopes: ['source.ruby', 'constant.numeric.ruby']});

    ({tokens} = grammar.tokenizeLine('0XA_FFF'));
    expect(tokens[0]).toEqual({value: '0XA_FFF', scopes: ['source.ruby', 'constant.numeric.ruby']});
});

  it("tokenizes octal numbers", function() {
    let {tokens} = grammar.tokenizeLine('01_777');
    expect(tokens[0]).toEqual({value: '01_777', scopes: ['source.ruby', 'constant.numeric.ruby']});

    ({tokens} = grammar.tokenizeLine('0o1_777'));
    expect(tokens[0]).toEqual({value: '0o1_777', scopes: ['source.ruby', 'constant.numeric.ruby']});
});

  it("tokenizes binary numbers", function() {
    let {tokens} = grammar.tokenizeLine('0b100_000');
    expect(tokens[0]).toEqual({value: '0b100_000', scopes: ['source.ruby', 'constant.numeric.ruby']});

    ({tokens} = grammar.tokenizeLine('0B00100'));
    expect(tokens[0]).toEqual({value: '0B00100', scopes: ['source.ruby', 'constant.numeric.ruby']});
});

  it("tokenizes symbols", function() {
    let {tokens} = grammar.tokenizeLine(':test');
    expect(tokens[0]).toEqual({value: ':', scopes: ['source.ruby', 'constant.other.symbol.ruby', 'punctuation.definition.constant.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'constant.other.symbol.ruby']});

    ({tokens} = grammar.tokenizeLine(':$symbol'));
    expect(tokens[0]).toEqual({value: ':', scopes: ['source.ruby', 'constant.other.symbol.ruby', 'punctuation.definition.constant.ruby']});
    expect(tokens[1]).toEqual({value: '$symbol', scopes: ['source.ruby', 'constant.other.symbol.ruby']});

    ({tokens} = grammar.tokenizeLine(':<=>'));
    expect(tokens[0]).toEqual({value: ':', scopes: ['source.ruby', 'constant.other.symbol.ruby', 'punctuation.definition.constant.ruby']});
    expect(tokens[1]).toEqual({value: '<=>', scopes: ['source.ruby', 'constant.other.symbol.ruby']});
});

  it("tokenizes symbol as hash key (1.9 syntax)", function() {
    const {tokens} = grammar.tokenizeLine('foo: 1');
    expect(tokens[0]).toEqual({value: 'foo', scopes: ['source.ruby', 'constant.other.symbol.hashkey.ruby']});
    expect(tokens[1]).toEqual({value: ':', scopes: ['source.ruby', 'constant.other.symbol.hashkey.ruby', 'punctuation.definition.constant.hashkey.ruby']});
    expect(tokens[2]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[3]).toEqual({value: '1', scopes: ['source.ruby', 'constant.numeric.ruby']});
});

  it("tokenizes symbol as hash key (1.8 syntax)", function() {
    const {tokens} = grammar.tokenizeLine(':foo => 1');
    expect(tokens[0]).toEqual({value: ':', scopes: ['source.ruby', 'constant.other.symbol.hashkey.ruby', 'punctuation.definition.constant.ruby']});
    expect(tokens[1]).toEqual({value: 'foo', scopes: ['source.ruby', 'constant.other.symbol.hashkey.ruby']});
    expect(tokens[2]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[3]).toEqual({value: '=>', scopes: ['source.ruby', 'punctuation.separator.key-value.ruby']});
    expect(tokens[4]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[5]).toEqual({value: '1', scopes: ['source.ruby', 'constant.numeric.ruby']});
});

  it("tokenizes :: separators", function() {
    let {tokens} = grammar.tokenizeLine('File::read "test"');
    expect(tokens[0]).toEqual({value: 'File', scopes: ['source.ruby', 'support.class.ruby']});
    expect(tokens[1]).toEqual({value: '::', scopes: ['source.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[2]).toEqual({value: 'read ', scopes: ['source.ruby']});

    ({tokens} = grammar.tokenizeLine('File:: read "test"'));
    expect(tokens[0]).toEqual({value: 'File', scopes: ['source.ruby', 'variable.other.constant.ruby']});
    expect(tokens[1]).toEqual({value: '::', scopes: ['source.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[2]).toEqual({value: ' read ', scopes: ['source.ruby']});

    ({tokens} = grammar.tokenizeLine('RbConfig::CONFIG'));
    expect(tokens[0]).toEqual({value: 'RbConfig', scopes: ['source.ruby', 'support.class.ruby']});
    expect(tokens[1]).toEqual({value: '::', scopes: ['source.ruby', 'punctuation.separator.namespace.ruby']});
    expect(tokens[2]).toEqual({value: 'CONFIG', scopes: ['source.ruby', 'variable.other.constant.ruby']});

    ({tokens} = grammar.tokenizeLine('RbConfig:: CONFIG'));
    expect(tokens[0]).toEqual({value: 'RbConfig', scopes: ['source.ruby', 'variable.other.constant.ruby']});
    expect(tokens[1]).toEqual({value: '::', scopes: ['source.ruby', 'punctuation.separator.namespace.ruby']});
    expect(tokens[2]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[3]).toEqual({value: 'CONFIG', scopes: ['source.ruby', 'variable.other.constant.ruby']});

    ({tokens} = grammar.tokenizeLine('class A::B::C < ::D::E'));
    expect(tokens[0]).toEqual({value: 'class', scopes: ['source.ruby', 'meta.class.ruby', 'keyword.control.class.ruby']});
    expect(tokens[2]).toEqual({value: 'A', scopes: ['source.ruby', 'meta.class.ruby', 'entity.name.type.class.ruby']});
    expect(tokens[3]).toEqual({value: '::', scopes: ['source.ruby', 'meta.class.ruby', 'punctuation.separator.namespace.ruby']});
    expect(tokens[4]).toEqual({value: 'B', scopes: ['source.ruby', 'meta.class.ruby', 'entity.name.type.class.ruby']});
    expect(tokens[5]).toEqual({value: '::', scopes: ['source.ruby', 'meta.class.ruby', 'punctuation.separator.namespace.ruby']});
    expect(tokens[6]).toEqual({value: 'C', scopes: ['source.ruby', 'meta.class.ruby', 'entity.name.type.class.ruby']});
    expect(tokens[8]).toEqual({value: '<', scopes: ['source.ruby', 'meta.class.ruby', 'punctuation.separator.inheritance.ruby']});
    expect(tokens[10]).toEqual({value: '::', scopes: ['source.ruby', 'meta.class.ruby', 'entity.other.inherited-class.ruby', 'punctuation.separator.namespace.ruby']});
    expect(tokens[11]).toEqual({value: 'D', scopes: ['source.ruby', 'meta.class.ruby', 'entity.other.inherited-class.ruby', 'entity.name.type.class.ruby']});
    expect(tokens[12]).toEqual({value: '::', scopes: ['source.ruby', 'meta.class.ruby', 'entity.other.inherited-class.ruby', 'punctuation.separator.namespace.ruby']});
    expect(tokens[13]).toEqual({value: 'E', scopes: ['source.ruby', 'meta.class.ruby', 'entity.other.inherited-class.ruby', 'entity.name.type.class.ruby']});

    ({tokens} = grammar.tokenizeLine('class << A::B'));
    expect(tokens[0]).toEqual({value: 'class', scopes: ['source.ruby', 'meta.class.ruby', 'keyword.control.class.ruby']});
    expect(tokens[2]).toEqual({value: '<<', scopes: ['source.ruby', 'meta.class.ruby', 'punctuation.definition.variable.ruby']});
    expect(tokens[4]).toEqual({value: 'A', scopes: ['source.ruby', 'meta.class.ruby', 'variable.other.object.ruby', 'entity.name.type.class.ruby']});
    expect(tokens[5]).toEqual({value: '::', scopes: ['source.ruby', 'meta.class.ruby', 'variable.other.object.ruby', 'punctuation.separator.namespace.ruby']});
    expect(tokens[6]).toEqual({value: 'B', scopes: ['source.ruby', 'meta.class.ruby', 'variable.other.object.ruby', 'entity.name.type.class.ruby']});

    ({tokens} = grammar.tokenizeLine('module A::B::C'));
    expect(tokens[0]).toEqual({value: 'module', scopes: ['source.ruby', 'meta.module.ruby', 'keyword.control.module.ruby']});
    expect(tokens[2]).toEqual({value: 'A', scopes: ['source.ruby', 'meta.module.ruby', 'entity.other.inherited-class.module.ruby']});
    expect(tokens[3]).toEqual({value: '::', scopes: ['source.ruby', 'meta.module.ruby', 'punctuation.separator.namespace.ruby']});
    expect(tokens[4]).toEqual({value: 'B', scopes: ['source.ruby', 'meta.module.ruby', 'entity.other.inherited-class.module.ruby']});
    expect(tokens[5]).toEqual({value: '::', scopes: ['source.ruby', 'meta.module.ruby', 'punctuation.separator.namespace.ruby']});
    expect(tokens[6]).toEqual({value: 'C', scopes: ['source.ruby', 'meta.module.ruby', 'entity.name.type.module.ruby']});
});

  it("tokenizes . separator", function() {
    let {tokens} = grammar.tokenizeLine('File.read "test"');
    expect(tokens[0]).toEqual({value: 'File', scopes: ['source.ruby', 'support.class.ruby']});
    expect(tokens[1]).toEqual({value: '.', scopes: ['source.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[2]).toEqual({value: 'read ', scopes: ['source.ruby']});

    ({tokens} = grammar.tokenizeLine('File. read "test"'));
    expect(tokens[0]).toEqual({value: 'File', scopes: ['source.ruby', 'variable.other.constant.ruby']});
    expect(tokens[1]).toEqual({value: '.', scopes: ['source.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[2]).toEqual({value: ' read ', scopes: ['source.ruby']});

    ({tokens} = grammar.tokenizeLine('def a.b(*args)'));
    expect(tokens[0]).toEqual({value: 'def', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'keyword.control.def.ruby']});
    expect(tokens[2]).toEqual({value: 'a', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[3]).toEqual({value: '.', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[4]).toEqual({value: 'b', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[5]).toEqual({value: '(', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.definition.parameters.ruby']});
    expect(tokens[6]).toEqual({value: '*', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'storage.type.variable.ruby']});
    expect(tokens[7]).toEqual({value: 'args', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'variable.parameter.function.ruby']});
    expect(tokens[8]).toEqual({value: ')', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.definition.parameters.ruby']});

    ({tokens} = grammar.tokenizeLine('def a.b *args'));
    expect(tokens[0]).toEqual({value: 'def', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'keyword.control.def.ruby']});
    expect(tokens[2]).toEqual({value: 'a', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[3]).toEqual({value: '.', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[4]).toEqual({value: 'b', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[6]).toEqual({value: '*', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'storage.type.variable.ruby']});
    expect(tokens[7]).toEqual({value: 'args', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'variable.parameter.function.ruby']});

    ({tokens} = grammar.tokenizeLine('def a.b'));
    expect(tokens[0]).toEqual({value: 'def', scopes: ['source.ruby', 'meta.function.method.without-arguments.ruby', 'keyword.control.def.ruby']});
    expect(tokens[2]).toEqual({value: 'a', scopes: ['source.ruby', 'meta.function.method.without-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[3]).toEqual({value: '.', scopes: ['source.ruby', 'meta.function.method.without-arguments.ruby', 'entity.name.function.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[4]).toEqual({value: 'b', scopes: ['source.ruby', 'meta.function.method.without-arguments.ruby', 'entity.name.function.ruby']});

    ({tokens} = grammar.tokenizeLine('def self.b(*args)'));
    expect(tokens[0]).toEqual({value: 'def', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'keyword.control.def.ruby']});
    expect(tokens[2]).toEqual({value: 'self', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby', 'variable.language.self.ruby']});
    expect(tokens[3]).toEqual({value: '.', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[4]).toEqual({value: 'b', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[5]).toEqual({value: '(', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.definition.parameters.ruby']});
    expect(tokens[6]).toEqual({value: '*', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'storage.type.variable.ruby']});
    expect(tokens[7]).toEqual({value: 'args', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'variable.parameter.function.ruby']});
    expect(tokens[8]).toEqual({value: ')', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.definition.parameters.ruby']});

    ({tokens} = grammar.tokenizeLine('def self.b *args'));
    expect(tokens[0]).toEqual({value: 'def', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'keyword.control.def.ruby']});
    expect(tokens[2]).toEqual({value: 'self', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby', 'variable.language.self.ruby']});
    expect(tokens[3]).toEqual({value: '.', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[4]).toEqual({value: 'b', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[6]).toEqual({value: '*', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'storage.type.variable.ruby']});
    expect(tokens[7]).toEqual({value: 'args', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'variable.parameter.function.ruby']});

    ({tokens} = grammar.tokenizeLine('def self.b'));
    expect(tokens[0]).toEqual({value: 'def', scopes: ['source.ruby', 'meta.function.method.without-arguments.ruby', 'keyword.control.def.ruby']});
    expect(tokens[2]).toEqual({value: 'self', scopes: ['source.ruby', 'meta.function.method.without-arguments.ruby', 'entity.name.function.ruby', 'variable.language.self.ruby']});
    expect(tokens[3]).toEqual({value: '.', scopes: ['source.ruby', 'meta.function.method.without-arguments.ruby', 'entity.name.function.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[4]).toEqual({value: 'b', scopes: ['source.ruby', 'meta.function.method.without-arguments.ruby', 'entity.name.function.ruby']});
});

  it("tokenizes , separator", function() {
    let {tokens} = grammar.tokenizeLine('hash = {1 => \'one\', 2 => \'two\'}');
    expect(tokens[0]).toEqual({value: 'hash ', scopes: ['source.ruby']});
    expect(tokens[1]).toEqual({value: '=', scopes: ['source.ruby', 'keyword.operator.assignment.ruby']});
    expect(tokens[3]).toEqual({value: '{', scopes: ['source.ruby', 'punctuation.section.scope.begin.ruby']});
    expect(tokens[4]).toEqual({value: '1', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[6]).toEqual({value: '=>', scopes: ['source.ruby', 'punctuation.separator.key-value.ruby']});
    expect(tokens[8]).toEqual({value: '\'', scopes: ['source.ruby', 'string.quoted.single.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(tokens[9]).toEqual({value: 'one', scopes: ['source.ruby', 'string.quoted.single.ruby']});
    expect(tokens[10]).toEqual({value: '\'', scopes: ['source.ruby', 'string.quoted.single.ruby', 'punctuation.definition.string.end.ruby']});
    expect(tokens[11]).toEqual({value: ',', scopes: ['source.ruby', 'punctuation.separator.delimiter.ruby']});
    expect(tokens[13]).toEqual({value: '2', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[15]).toEqual({value: '=>', scopes: ['source.ruby', 'punctuation.separator.key-value.ruby']});
    expect(tokens[17]).toEqual({value: '\'', scopes: ['source.ruby', 'string.quoted.single.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(tokens[18]).toEqual({value: 'two', scopes: ['source.ruby', 'string.quoted.single.ruby']});
    expect(tokens[19]).toEqual({value: '\'', scopes: ['source.ruby', 'string.quoted.single.ruby', 'punctuation.definition.string.end.ruby']});
    expect(tokens[20]).toEqual({value: '}', scopes: ['source.ruby', 'punctuation.section.scope.end.ruby']});

    ({tokens} = grammar.tokenizeLine('method(a,b)'));
    expect(tokens[0]).toEqual({value: 'method', scopes: ['source.ruby']});
    expect(tokens[1]).toEqual({value: '(', scopes: ['source.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[2]).toEqual({value: 'a', scopes: ['source.ruby']});
    expect(tokens[3]).toEqual({value: ',', scopes: ['source.ruby', 'punctuation.separator.delimiter.ruby']});
    expect(tokens[4]).toEqual({value: 'b', scopes: ['source.ruby']});
    expect(tokens[5]).toEqual({value: ')', scopes: ['source.ruby', 'punctuation.section.function.ruby']});
});


  it("tokenizes %() style strings", function() {
    const {tokens} = grammar.tokenizeLine('%(te(s)t)');

    expect(tokens[0]).toEqual({value: '%(', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(tokens[1]).toEqual({value: 'te', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '(', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.section.scope.ruby']});
    expect(tokens[3]).toEqual({value: 's', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[4]).toEqual({value: ')', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.section.scope.ruby']});
    expect(tokens[5]).toEqual({value: 't', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[6]).toEqual({value: ')', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.end.ruby']});
});

  it("tokenizes %[] style strings", function() {
    const {tokens} = grammar.tokenizeLine('%[te[s]t]');

    expect(tokens[0]).toEqual({value: '%[', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(tokens[1]).toEqual({value: 'te', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '[', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.section.scope.ruby']});
    expect(tokens[3]).toEqual({value: 's', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[4]).toEqual({value: ']', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.section.scope.ruby']});
    expect(tokens[5]).toEqual({value: 't', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[6]).toEqual({value: ']', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.end.ruby']});
});

  it("tokenizes %{} style strings", function() {
    const {tokens} = grammar.tokenizeLine('%{te{s}t}');

    expect(tokens[0]).toEqual({value: '%{', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(tokens[1]).toEqual({value: 'te', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '{', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.section.scope.ruby']});
    expect(tokens[3]).toEqual({value: 's', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[4]).toEqual({value: '}', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.section.scope.ruby']});
    expect(tokens[5]).toEqual({value: 't', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[6]).toEqual({value: '}', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.end.ruby']});
});

  it("tokenizes %<> style strings", function() {
    const {tokens} = grammar.tokenizeLine('%<te<s>t>');

    expect(tokens[0]).toEqual({value: '%<', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(tokens[1]).toEqual({value: 'te', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '<', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.section.scope.ruby']});
    expect(tokens[3]).toEqual({value: 's', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[4]).toEqual({value: '>', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.section.scope.ruby']});
    expect(tokens[5]).toEqual({value: 't', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[6]).toEqual({value: '>', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.end.ruby']});
});

  it("tokenizes %~~ style strings", function() {
    const {tokens} = grammar.tokenizeLine('%~te\\~s\\~t~');

    expect(tokens[0]).toEqual({value: '%~', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(tokens[1]).toEqual({value: 'te', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '\\~', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'constant.character.escape.ruby']});
    expect(tokens[3]).toEqual({value: 's', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[4]).toEqual({value: '\\~', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'constant.character.escape.ruby']});
    expect(tokens[5]).toEqual({value: 't', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[6]).toEqual({value: '~', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.end.ruby']});
});

  it("tokenizes %Q() style strings", function() {
    const {tokens} = grammar.tokenizeLine('%Q(te(s)t)');

    expect(tokens[0]).toEqual({value: '%Q(', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(tokens[1]).toEqual({value: 'te', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '(', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.section.scope.ruby']});
    expect(tokens[3]).toEqual({value: 's', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[4]).toEqual({value: ')', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.section.scope.ruby']});
    expect(tokens[5]).toEqual({value: 't', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby']});
    expect(tokens[6]).toEqual({value: ')', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.end.ruby']});
});

  it("tokenizes %x!! style strings", function() {
    const {tokens} = grammar.tokenizeLine('%x!\#\{"l" + "s"\}!');

    expect(tokens[0]).toEqual({value: '%x!', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(tokens[1]).toEqual({value: '#{', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'meta.embedded.line.ruby', 'punctuation.section.embedded.begin.ruby']});
    expect(tokens[11]).toEqual({value: '}', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'meta.embedded.line.ruby', 'punctuation.section.embedded.end.ruby']});
    expect(tokens[12]).toEqual({value: '!', scopes: ['source.ruby', 'string.quoted.other.interpolated.ruby', 'punctuation.definition.string.end.ruby']});
});

  it("tokenizes regular expressions", function() {
    let {tokens} = grammar.tokenizeLine('/test/');

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});

    ({tokens} = grammar.tokenizeLine('/{w}/'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: '{w}', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});

    ({tokens} = grammar.tokenizeLine('a_method /test/'));

    expect(tokens[0]).toEqual({value: 'a_method ', scopes: ['source.ruby']});
    expect(tokens[1]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[2]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[3]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});

    ({tokens} = grammar.tokenizeLine('a_method(/test/)'));

    expect(tokens[0]).toEqual({value: 'a_method', scopes: ['source.ruby']});
    expect(tokens[1]).toEqual({value: '(', scopes: ['source.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[4]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[5]).toEqual({value: ')', scopes: ['source.ruby', 'punctuation.section.function.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/.match("test")'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: '.', scopes: ['source.ruby', 'punctuation.separator.method.ruby']});

    ({tokens} = grammar.tokenizeLine('foo(4 / 2).split(/c/)'));

    expect(tokens[0]).toEqual({value: 'foo', scopes: ['source.ruby']});
    expect(tokens[1]).toEqual({value: '(', scopes: ['source.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[2]).toEqual({value: '4', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: '/', scopes: ['source.ruby', 'keyword.operator.arithmetic.ruby']});
    expect(tokens[5]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[6]).toEqual({value: '2', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[7]).toEqual({value: ')', scopes: ['source.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[8]).toEqual({value: '.', scopes: ['source.ruby', 'punctuation.separator.method.ruby']});
    expect(tokens[9]).toEqual({value: 'split', scopes: ['source.ruby']});
    expect(tokens[10]).toEqual({value: '(', scopes: ['source.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[11]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[12]).toEqual({value: 'c', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[13]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[14]).toEqual({value: ')', scopes: ['source.ruby', 'punctuation.section.function.ruby']});

    ({tokens} = grammar.tokenizeLine('[/test/,3]'));

    expect(tokens[0]).toEqual({value: '[', scopes: ['source.ruby', 'punctuation.section.array.begin.ruby']});
    expect(tokens[1]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[2]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[3]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[4]).toEqual({value: ',', scopes: ['source.ruby', 'punctuation.separator.delimiter.ruby']});

    ({tokens} = grammar.tokenizeLine('[/test/]'));

    expect(tokens[0]).toEqual({value: '[', scopes: ['source.ruby', 'punctuation.section.array.begin.ruby']});
    expect(tokens[1]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[2]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[3]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ && 4'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ || 4'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ ? 4 : 3'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ : foo'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});

    ({tokens} = grammar.tokenizeLine('{a: /test/}'));

    expect(tokens[4]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[5]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[6]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});

    ({tokens} = grammar.tokenizeLine('if "test" =~ /test/ then 4 end'));

    expect(tokens[8]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[9]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[10]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[11]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[12]).toEqual({value: 'then', scopes: ['source.ruby', 'keyword.control.ruby']});

    ({tokens} = grammar.tokenizeLine('if "test" =~ /test/; 4 end'));

    expect(tokens[8]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[9]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[10]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[11]).toEqual({value: ';', scopes: ['source.ruby', 'punctuation.terminator.statement.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ =~ "test"'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: '=~', scopes: ['source.ruby', 'keyword.operator.comparison.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ !~ "test"'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: '!~', scopes: ['source.ruby', 'keyword.operator.comparison.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ != "test"'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: '!=', scopes: ['source.ruby', 'keyword.operator.comparison.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ == /test/'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: '==', scopes: ['source.ruby', 'keyword.operator.comparison.ruby']});
    expect(tokens[5]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[6]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[7]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[8]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ === /test/'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: '===', scopes: ['source.ruby', 'keyword.operator.comparison.ruby']});
    expect(tokens[5]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[6]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[7]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[8]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});

    ({tokens} = grammar.tokenizeLine('if false then /test/ else 4 end'));

    expect(tokens[4]).toEqual({value: 'then', scopes: ['source.ruby', 'keyword.control.ruby']});
    expect(tokens[5]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[6]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[7]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[8]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[9]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[10]).toEqual({value: 'else', scopes: ['source.ruby', 'keyword.control.ruby']});

    ({tokens} = grammar.tokenizeLine('if false then 4 else /test/ end'));

    expect(tokens[8]).toEqual({value: 'else', scopes: ['source.ruby', 'keyword.control.ruby']});
    expect(tokens[9]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[10]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[11]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[12]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[13]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[14]).toEqual({value: 'end', scopes: ['source.ruby', 'keyword.control.ruby']});

    ({tokens} = grammar.tokenizeLine('if true then /test/ elsif false then 4 end'));

    expect(tokens[4]).toEqual({value: 'then', scopes: ['source.ruby', 'keyword.control.ruby']});
    expect(tokens[5]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[6]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[7]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[8]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[9]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[10]).toEqual({value: 'elsif', scopes: ['source.ruby', 'keyword.control.ruby']});

    ({tokens} = grammar.tokenizeLine('method /test/ do; end'));

    expect(tokens[1]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[2]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[3]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[4]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[5]).toEqual({value: 'do', scopes: ['source.ruby', 'keyword.control.start-block.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ if true'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: 'if', scopes: ['source.ruby', 'keyword.control.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ unless true'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: 'unless', scopes: ['source.ruby', 'keyword.control.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ while true'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: 'while', scopes: ['source.ruby', 'keyword.control.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ until true'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: 'until', scopes: ['source.ruby', 'keyword.control.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ or return'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: 'or', scopes: ['source.ruby', 'keyword.operator.logical.ruby']});
    expect(tokens[5]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[6]).toEqual({value: 'return', scopes: ['source.ruby', 'keyword.control.pseudo-method.ruby']});

    ({tokens} = grammar.tokenizeLine('/test/ and return'));

    expect(tokens[0]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: 'and', scopes: ['source.ruby', 'keyword.operator.logical.ruby']});
    expect(tokens[5]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[6]).toEqual({value: 'return', scopes: ['source.ruby', 'keyword.control.pseudo-method.ruby']});

    ({tokens} = grammar.tokenizeLine('{/test/ => 1}'));

    expect(tokens[1]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[2]).toEqual({value: 'test', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[3]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[4]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[5]).toEqual({value: '=>', scopes: ['source.ruby', 'punctuation.separator.key-value.ruby']});
    expect(tokens[6]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[7]).toEqual({value: '1', scopes: ['source.ruby', 'constant.numeric.ruby']});
});

  it("tokenizes multiline regular expressions", function() {
    const tokens = grammar.tokenizeLines(`\
regexp = /
  foo|
  bar
/ix\
`
    );

    expect(tokens[0][0]).toEqual({value: 'regexp ', scopes: ['source.ruby']});
    expect(tokens[0][1]).toEqual({value: '=', scopes: ['source.ruby', 'keyword.operator.assignment.ruby']});
    expect(tokens[0][2]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[0][3]).toEqual({value: '/', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
    expect(tokens[1][0]).toEqual({value: '  foo|', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[2][0]).toEqual({value: '  bar', scopes: ['source.ruby', 'string.regexp.interpolated.ruby']});
    expect(tokens[3][0]).toEqual({value: '/ix', scopes: ['source.ruby', 'string.regexp.interpolated.ruby', 'punctuation.section.regexp.ruby']});
});

  it("tokenizes the / arithmetic operator", function() {
    let {tokens} = grammar.tokenizeLine('call/me/maybe');
    expect(tokens[0]).toEqual({value: 'call', scopes: ['source.ruby']});
    expect(tokens[1]).toEqual({value: '/', scopes: ['source.ruby', 'keyword.operator.arithmetic.ruby']});
    expect(tokens[2]).toEqual({value: 'me', scopes: ['source.ruby']});
    expect(tokens[3]).toEqual({value: '/', scopes: ['source.ruby', 'keyword.operator.arithmetic.ruby']});
    expect(tokens[4]).toEqual({value: 'maybe', scopes: ['source.ruby']});

    ({tokens} = grammar.tokenizeLine('(1+2)/3/4'));
    expect(tokens[0]).toEqual({value: '(', scopes: ['source.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[1]).toEqual({value: '1', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[2]).toEqual({value: '+', scopes: ['source.ruby', 'keyword.operator.arithmetic.ruby']});
    expect(tokens[3]).toEqual({value: '2', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[4]).toEqual({value: ')', scopes: ['source.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[5]).toEqual({value: '/', scopes: ['source.ruby', 'keyword.operator.arithmetic.ruby']});
    expect(tokens[6]).toEqual({value: '3', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[7]).toEqual({value: '/', scopes: ['source.ruby', 'keyword.operator.arithmetic.ruby']});
    expect(tokens[8]).toEqual({value: '4', scopes: ['source.ruby', 'constant.numeric.ruby']});

    ({tokens} = grammar.tokenizeLine('1 / 2 / 3'));
    expect(tokens[0]).toEqual({value: '1', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[1]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'keyword.operator.arithmetic.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: '2', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[5]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[6]).toEqual({value: '/', scopes: ['source.ruby', 'keyword.operator.arithmetic.ruby']});
    expect(tokens[7]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[8]).toEqual({value: '3', scopes: ['source.ruby', 'constant.numeric.ruby']});

    ({tokens} = grammar.tokenizeLine('1/ 2 / 3'));
    expect(tokens[0]).toEqual({value: '1', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[1]).toEqual({value: '/', scopes: ['source.ruby', 'keyword.operator.arithmetic.ruby']});
    expect(tokens[2]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[3]).toEqual({value: '2', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[4]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[5]).toEqual({value: '/', scopes: ['source.ruby', 'keyword.operator.arithmetic.ruby']});
    expect(tokens[6]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[7]).toEqual({value: '3', scopes: ['source.ruby', 'constant.numeric.ruby']});

    ({tokens} = grammar.tokenizeLine('1 / 2/ 3'));
    expect(tokens[0]).toEqual({value: '1', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[1]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[2]).toEqual({value: '/', scopes: ['source.ruby', 'keyword.operator.arithmetic.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[4]).toEqual({value: '2', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[5]).toEqual({value: '/', scopes: ['source.ruby', 'keyword.operator.arithmetic.ruby']});
    expect(tokens[6]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[7]).toEqual({value: '3', scopes: ['source.ruby', 'constant.numeric.ruby']});

    ({tokens} = grammar.tokenizeLine('x / 2; x /= 2'));
    expect(tokens[1]).toEqual({value: '/', scopes: ['source.ruby', 'keyword.operator.arithmetic.ruby']});
    expect(tokens[2]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[3]).toEqual({value: '2', scopes: ['source.ruby', 'constant.numeric.ruby']});
    expect(tokens[4]).toEqual({value: ';', scopes: ['source.ruby', 'punctuation.terminator.statement.ruby']});
    expect(tokens[6]).toEqual({value: '/=', scopes: ['source.ruby', 'keyword.operator.assignment.augmented.ruby']});
    expect(tokens[7]).toEqual({value: ' ', scopes: ['source.ruby']});
    expect(tokens[8]).toEqual({value: '2', scopes: ['source.ruby', 'constant.numeric.ruby']});
});

  it("tokenizes 'not' when used as method name", function() {
    let {tokens} = grammar.tokenizeLine('foo.not(bar)');
    expect(tokens[2]).toEqual({value: 'not', scopes: ['source.ruby']});

    ({tokens} = grammar.tokenizeLine('not?(Array)'));
    expect(tokens[0]).toEqual({value: 'not?', scopes: ['source.ruby']});
});

  it("tokenizes 'not' as logical operator", function() {
    const {tokens} = grammar.tokenizeLine('not true');
    expect(tokens[0]).toEqual({value: 'not', scopes: ['source.ruby', 'keyword.operator.logical.ruby']});
});

  it("tokenizes ! when used in method name", function() {
    const {tokens} = grammar.tokenizeLine('sort!');
    expect(tokens[0]).toEqual({value: 'sort!', scopes: ['source.ruby']});
});

  it("tokenizes ! as logical operator", function() {
    const {tokens} = grammar.tokenizeLine('!foo');
    expect(tokens[0]).toEqual({value: '!', scopes: ['source.ruby', 'keyword.operator.logical.ruby']});
});

  it("tokenizes != as comparison operator", function() {
    const {tokens} = grammar.tokenizeLine('foo != bar');
    expect(tokens[1]).toEqual({value: '!=', scopes: ['source.ruby', 'keyword.operator.comparison.ruby']});
});

  it("tokenizes yard documentation comments", function() {
    let {tokens} = grammar.tokenizeLine('# @private');
    expect(tokens[0]).toEqual({value: '#', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'punctuation.definition.comment.ruby']});
    expect(tokens[1]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby']});
    expect(tokens[2]).toEqual({value: '@', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.punctuation.yard.ruby']});
    expect(tokens[3]).toEqual({value: 'private', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.yard.ruby']});

    tokens = grammar.tokenizeLines(`\
# @deprecated Because I said so,
#   end of discussion\
`
    );
    expect(tokens[0][0]).toEqual({value: '#', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'punctuation.definition.comment.ruby']});
    expect(tokens[0][1]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby']});
    expect(tokens[0][2]).toEqual({value: '@', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.punctuation.yard.ruby']});
    expect(tokens[0][3]).toEqual({value: 'deprecated', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.yard.ruby']});
    expect(tokens[0][4]).toEqual({value: ' Because I said so,', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby']});
    expect(tokens[1][0]).toEqual({value: '#', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby', 'punctuation.definition.comment.ruby']});
    expect(tokens[1][1]).toEqual({value: '   end of discussion', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby']});

    tokens = grammar.tokenizeLines(`\
# @raise [AccountBalanceError] if the account does not have
#   sufficient funds to perform the transaction\
`
    );
    expect(tokens[0][0]).toEqual({value: '#', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'punctuation.definition.comment.ruby']});
    expect(tokens[0][1]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby']});
    expect(tokens[0][2]).toEqual({value: '@', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.punctuation.yard.ruby']});
    expect(tokens[0][3]).toEqual({value: 'raise', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.yard.ruby']});
    expect(tokens[0][4]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby']});
    expect(tokens[0][5]).toEqual({value: '[', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby', 'comment.line.punctuation.yard.ruby']});
    expect(tokens[0][6]).toEqual({value: 'AccountBalanceError', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby']});
    expect(tokens[0][7]).toEqual({value: ']', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby', 'comment.line.punctuation.yard.ruby']});
    expect(tokens[0][8]).toEqual({value: ' if the account does not have', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby']});
    expect(tokens[1][0]).toEqual({value: '#', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby', 'punctuation.definition.comment.ruby']});
    expect(tokens[1][1]).toEqual({value: '   sufficient funds to perform the transaction', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby']});

    tokens = grammar.tokenizeLines(`\
# @param value [Object] describe value param in a long way which
#   makes it multiline\
`
    );
    expect(tokens[0][0]).toEqual({value: '#', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'punctuation.definition.comment.ruby']});
    expect(tokens[0][1]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby']});
    expect(tokens[0][2]).toEqual({value: '@', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.punctuation.yard.ruby']});
    expect(tokens[0][3]).toEqual({value: 'param', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.yard.ruby']});
    expect(tokens[0][4]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby']});
    expect(tokens[0][5]).toEqual({value: 'value', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.parameter.yard.ruby']});
    expect(tokens[0][6]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby']});
    expect(tokens[0][7]).toEqual({value: '[', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby', 'comment.line.punctuation.yard.ruby']});
    expect(tokens[0][8]).toEqual({value: 'Object', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby']});
    expect(tokens[0][9]).toEqual({value: ']', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby', 'comment.line.punctuation.yard.ruby']});
    expect(tokens[0][10]).toEqual({value: ' describe value param in a long way which', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby']});
    expect(tokens[1][0]).toEqual({value: '#', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby', 'punctuation.definition.comment.ruby']});
    expect(tokens[1][1]).toEqual({value: '   makes it multiline', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby']});

    ({tokens} = grammar.tokenizeLine('# @param [Bar] Baz'));
    expect(tokens[0]).toEqual({value: '#', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'punctuation.definition.comment.ruby']});
    expect(tokens[1]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby']});
    expect(tokens[2]).toEqual({value: '@', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.punctuation.yard.ruby']});
    expect(tokens[3]).toEqual({value: 'param', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.yard.ruby']});
    expect(tokens[4]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby']});
    expect(tokens[5]).toEqual({value: '[', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby', 'comment.line.punctuation.yard.ruby']});
    expect(tokens[6]).toEqual({value: 'Bar', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby']});
    expect(tokens[7]).toEqual({value: ']', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby', 'comment.line.punctuation.yard.ruby']});
    expect(tokens[8]).toEqual({value: ' Baz', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby']});

    ({tokens} = grammar.tokenizeLine('# @return [Array#[](0), Array] comment'));
    expect(tokens[0]).toEqual({value: '#', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'punctuation.definition.comment.ruby']});
    expect(tokens[1]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby']});
    expect(tokens[2]).toEqual({value: '@', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.punctuation.yard.ruby']});
    expect(tokens[3]).toEqual({value: 'return', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.yard.ruby']});
    expect(tokens[4]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby']});
    expect(tokens[5]).toEqual({value: '[', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby', 'comment.line.punctuation.yard.ruby']});
    expect(tokens[6]).toEqual({value: 'Array#[](0), Array', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby']});
    expect(tokens[7]).toEqual({value: ']', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby', 'comment.line.punctuation.yard.ruby']});
    expect(tokens[8]).toEqual({value: ' comment', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby']});

    ({tokens} = grammar.tokenizeLine('# @param [Array#[](0), Array] comment'));
    expect(tokens[0]).toEqual({value: '#', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'punctuation.definition.comment.ruby']});
    expect(tokens[1]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby']});
    expect(tokens[2]).toEqual({value: '@', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.punctuation.yard.ruby']});
    expect(tokens[3]).toEqual({value: 'param', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.yard.ruby']});
    expect(tokens[4]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby']});
    expect(tokens[5]).toEqual({value: '[', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby', 'comment.line.punctuation.yard.ruby']});
    expect(tokens[6]).toEqual({value: 'Array#[](0), Array', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby']});
    expect(tokens[7]).toEqual({value: ']', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby', 'comment.line.punctuation.yard.ruby']});
    expect(tokens[8]).toEqual({value: ' comment', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby']});

    tokens = grammar.tokenizeLines(`\
# @!attribute [r] count the number of items
#   present in the list\
`
    );
    expect(tokens[0][0]).toEqual({value: '#', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'punctuation.definition.comment.ruby']});
    expect(tokens[0][1]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby']});
    expect(tokens[0][2]).toEqual({value: '@!', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.punctuation.yard.ruby']});
    expect(tokens[0][3]).toEqual({value: 'attribute', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.keyword.yard.ruby']});
    expect(tokens[0][4]).toEqual({value: ' ', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby']});
    expect(tokens[0][5]).toEqual({value: '[', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby', 'comment.line.punctuation.yard.ruby']});
    expect(tokens[0][6]).toEqual({value: 'r', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby']});
    expect(tokens[0][7]).toEqual({value: ']', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.yard.ruby', 'comment.line.type.yard.ruby', 'comment.line.punctuation.yard.ruby']});
    expect(tokens[0][8]).toEqual({value: ' count the number of items', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby']});
    expect(tokens[1][0]).toEqual({value: '#', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby', 'punctuation.definition.comment.ruby']});
    expect(tokens[1][1]).toEqual({value: '   present in the list', scopes: ['source.ruby', 'comment.line.number-sign.ruby', 'comment.line.string.yard.ruby']});
});

  it("tokenizes a method with *args properly", function() {
    let {tokens} = grammar.tokenizeLine('def method(*args)');
    expect(tokens[0]).toEqual({value: 'def', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'keyword.control.def.ruby']});
    expect(tokens[2]).toEqual({value: 'method', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[3]).toEqual({value: '(', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.definition.parameters.ruby']});
    expect(tokens[4]).toEqual({value: '*', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'storage.type.variable.ruby']});
    expect(tokens[5]).toEqual({value: 'args', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'variable.parameter.function.ruby']});
    expect(tokens[6]).toEqual({value: ')', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.definition.parameters.ruby']});

    ({tokens} = grammar.tokenizeLine('def method(args)'));
    expect(tokens[0]).toEqual({value: 'def', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'keyword.control.def.ruby']});
    expect(tokens[2]).toEqual({value: 'method', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[3]).toEqual({value: '(', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.definition.parameters.ruby']});
    expect(tokens[4]).toEqual({value: 'args', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'variable.parameter.function.ruby']});
    expect(tokens[5]).toEqual({value: ')', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.definition.parameters.ruby']});

    ({tokens} = grammar.tokenizeLine('def method *args'));
    expect(tokens[0]).toEqual({value: 'def', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'keyword.control.def.ruby']});
    expect(tokens[2]).toEqual({value: 'method', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[3]).toEqual({value: ' ', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby']});
    expect(tokens[4]).toEqual({value: '*', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'storage.type.variable.ruby']});
    expect(tokens[5]).toEqual({value: 'args', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'variable.parameter.function.ruby']});
});

  it("tokenizes a method with (symbol: arg) properly", function() {
    const {tokens} = grammar.tokenizeLine('def method(red: 2)');
    expect(tokens[0]).toEqual({value: 'def', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'keyword.control.def.ruby']});
    expect(tokens[2]).toEqual({value: 'method', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[3]).toEqual({value: '(', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.definition.parameters.ruby']});
    expect(tokens[4]).toEqual({value: 'red', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.other.symbol.hashkey.parameter.function.ruby']});
    expect(tokens[7]).toEqual({value: '2', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.numeric.ruby']});
});

  it("tokenizes a method with symbol: arg (no paren) properly", function() {
    const {tokens} = grammar.tokenizeLine('def method red: 2');
    expect(tokens[0]).toEqual({value: 'def', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'keyword.control.def.ruby']});
    expect(tokens[2]).toEqual({value: 'method', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[4]).toEqual({value: 'red', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.other.symbol.hashkey.parameter.function.ruby']});
    expect(tokens[7]).toEqual({value: '2', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.numeric.ruby']});
});

  it("tokenizes a method with (symbol: arg(paren), symbol: arg2(paren)...) properly", function() {
    const {tokens} = grammar.tokenizeLine('def method(red: rand(2), green: rand(3), blue: rand(4))');
    expect(tokens[0]).toEqual({value: 'def', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'keyword.control.def.ruby']});
    expect(tokens[2]).toEqual({value: 'method', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[3]).toEqual({value: '(', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.definition.parameters.ruby']});
    expect(tokens[4]).toEqual({value: 'red', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.other.symbol.hashkey.parameter.function.ruby']});
    expect(tokens[7]).toEqual({value: 'rand', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'support.function.kernel.ruby']});
    expect(tokens[8]).toEqual({value: '(', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[9]).toEqual({value: '2', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.numeric.ruby']});
    expect(tokens[10]).toEqual({value: ')', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[11]).toEqual({value: ',', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.separator.delimiter.ruby']});
    expect(tokens[13]).toEqual({value: 'green', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.other.symbol.hashkey.parameter.function.ruby']});
    expect(tokens[16]).toEqual({value: 'rand', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'support.function.kernel.ruby']});
    expect(tokens[17]).toEqual({value: '(', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[18]).toEqual({value: '3', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.numeric.ruby']});
    expect(tokens[19]).toEqual({value: ')', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[20]).toEqual({value: ',', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.separator.delimiter.ruby']});
    expect(tokens[22]).toEqual({value: 'blue', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.other.symbol.hashkey.parameter.function.ruby']});
    expect(tokens[25]).toEqual({value: 'rand', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'support.function.kernel.ruby']});
    expect(tokens[26]).toEqual({value: '(', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[27]).toEqual({value: '4', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.numeric.ruby']});
    expect(tokens[28]).toEqual({value: ')', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.section.function.ruby']});
});

  it("tokenizes a method with symbol: arg(paren), symbol: arg2(paren)... (no outer parens) properly", function() {
    const {tokens} = grammar.tokenizeLine('def method red: rand(2), green: rand(3), blue: rand(4)');
    expect(tokens[0]).toEqual({value: 'def', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'keyword.control.def.ruby']});
    expect(tokens[2]).toEqual({value: 'method', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'entity.name.function.ruby']});
    expect(tokens[4]).toEqual({value: 'red', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.other.symbol.hashkey.parameter.function.ruby']});
    expect(tokens[7]).toEqual({value: 'rand', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'support.function.kernel.ruby']});
    expect(tokens[8]).toEqual({value: '(', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[9]).toEqual({value: '2', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.numeric.ruby']});
    expect(tokens[10]).toEqual({value: ')', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[11]).toEqual({value: ',', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.separator.delimiter.ruby']});
    expect(tokens[13]).toEqual({value: 'green', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.other.symbol.hashkey.parameter.function.ruby']});
    expect(tokens[16]).toEqual({value: 'rand', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'support.function.kernel.ruby']});
    expect(tokens[17]).toEqual({value: '(', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[18]).toEqual({value: '3', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.numeric.ruby']});
    expect(tokens[19]).toEqual({value: ')', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[20]).toEqual({value: ',', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.separator.delimiter.ruby']});
    expect(tokens[22]).toEqual({value: 'blue', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.other.symbol.hashkey.parameter.function.ruby']});
    expect(tokens[25]).toEqual({value: 'rand', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'support.function.kernel.ruby']});
    expect(tokens[26]).toEqual({value: '(', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[27]).toEqual({value: '4', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'constant.numeric.ruby']});
    expect(tokens[28]).toEqual({value: ')', scopes: ['source.ruby', 'meta.function.method.with-arguments.ruby', 'punctuation.section.function.ruby']});
});

  it("tokenizes a stabby lambda properly", function() {
    const {tokens} = grammar.tokenizeLine('method_name -> { puts "A message"} do');
    expect(tokens[1]).toEqual({value: '->', scopes: ['source.ruby', 'support.function.kernel.arrow.ruby']});
});

  it("tokenizes a simple do block properly", function() {
    const {tokens} = grammar.tokenizeLine('do |foo| ');
    expect(tokens[0]).toEqual({value: 'do', scopes: ['source.ruby', 'keyword.control.start-block.ruby']});
    expect(tokens[2]).toEqual({value: '|', scopes: ['source.ruby', 'punctuation.separator.variable.ruby']});
    expect(tokens[3]).toEqual({value: 'foo', scopes: ['source.ruby', 'variable.other.block.ruby']});
    expect(tokens[4]).toEqual({value: '|', scopes: ['source.ruby', 'punctuation.separator.variable.ruby']});
});

  it("tokenizes a complex do block properly", function() {
    const {tokens} = grammar.tokenizeLine('do |key = (a || b), hash = config, create: false|');
    expect(tokens[0]).toEqual({value: 'do', scopes: ['source.ruby', 'keyword.control.start-block.ruby']});
    expect(tokens[2]).toEqual({value: '|', scopes: ['source.ruby', 'punctuation.separator.variable.ruby']});
    expect(tokens[3]).toEqual({value: 'key', scopes: ['source.ruby', 'variable.other.block.ruby']});
    expect(tokens[5]).toEqual({value: '=', scopes: ['source.ruby', 'keyword.operator.assignment.ruby']});
    expect(tokens[7]).toEqual({value: '(', scopes: ['source.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[8]).toEqual({value: 'a', scopes: ['source.ruby', 'variable.other.block.ruby']});
    expect(tokens[10]).toEqual({value: '||', scopes: ['source.ruby', 'keyword.operator.logical.ruby']});
    expect(tokens[12]).toEqual({value: 'b', scopes: ['source.ruby', 'variable.other.block.ruby']});
    expect(tokens[13]).toEqual({value: ')', scopes: ['source.ruby', 'punctuation.section.function.ruby']});
    expect(tokens[14]).toEqual({value: ',', scopes: ['source.ruby', 'punctuation.separator.delimiter.ruby']});
    expect(tokens[16]).toEqual({value: 'hash', scopes: ['source.ruby', 'variable.other.block.ruby']});
    expect(tokens[18]).toEqual({value: '=', scopes: ['source.ruby', 'keyword.operator.assignment.ruby']});
    expect(tokens[20]).toEqual({value: 'config', scopes: ['source.ruby', 'variable.other.block.ruby']});
    expect(tokens[21]).toEqual({value: ',', scopes: ['source.ruby', 'punctuation.separator.delimiter.ruby']});
    expect(tokens[23]).toEqual({value: 'create', scopes: ['source.ruby', 'constant.other.symbol.hashkey.ruby']});
    expect(tokens[24]).toEqual({value: ':', scopes: ['source.ruby', 'constant.other.symbol.hashkey.ruby', 'punctuation.definition.constant.hashkey.ruby']});
    expect(tokens[26]).toEqual({value: 'false', scopes: ['source.ruby', 'constant.language.boolean.ruby']});
    expect(tokens[27]).toEqual({value: '|', scopes: ['source.ruby', 'punctuation.separator.variable.ruby']});
});

  it("does not erroneously tokenize a variable ending in `do` followed by a pipe as a block", function() {
    const {tokens} = grammar.tokenizeLine('sudo ||= true');
    expect(tokens[0]).toEqual({value: 'sudo ', scopes: ['source.ruby']});
    expect(tokens[1]).toEqual({value: '||=', scopes: ['source.ruby', 'keyword.operator.assignment.augmented.ruby']});
    expect(tokens[3]).toEqual({value: 'true', scopes: ['source.ruby', 'constant.language.boolean.ruby']});
});

  it("tokenizes <<- heredoc", function() {
    const lines = grammar.tokenizeLines('<<-EOS\nThis is text\nEOS');
    expect(lines[0][0]).toEqual({value: '<<-EOS', scopes: ['source.ruby', 'string.unquoted.heredoc.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(lines[2][0]).toEqual({value: 'EOS', scopes: ['source.ruby', 'string.unquoted.heredoc.ruby', 'punctuation.definition.string.end.ruby']});
});

  it("tokenizes <<~ Ruby 2.3.0 squiggly heredoc", function() {
    const lines = grammar.tokenizeLines('<<~EOS\nThis is text\nEOS');
    expect(lines[0][0]).toEqual({value: '<<~EOS', scopes: ['source.ruby', 'string.unquoted.heredoc.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(lines[2][0]).toEqual({value: 'EOS', scopes: ['source.ruby', 'string.unquoted.heredoc.ruby', 'punctuation.definition.string.end.ruby']});
});

  it("tokenizes quoted heredoc", function() {
    // Double-quoted heredoc:
    let lines = grammar.tokenizeLines('<<~"EOS"\nThis is text\nEOS');
    expect(lines[0][0]).toEqual({value: '<<~"EOS"', scopes: ['source.ruby', 'string.unquoted.heredoc.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(lines[2][0]).toEqual({value: 'EOS', scopes: ['source.ruby', 'string.unquoted.heredoc.ruby', 'punctuation.definition.string.end.ruby']});
    // Single-quoted heredoc:
    lines = grammar.tokenizeLines('<<~\'EOS\'\nThis is text\nEOS');
    expect(lines[0][0]).toEqual({value: '<<~\'EOS\'', scopes: ['source.ruby', 'string.unquoted.heredoc.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(lines[2][0]).toEqual({value: 'EOS', scopes: ['source.ruby', 'string.unquoted.heredoc.ruby', 'punctuation.definition.string.end.ruby']});
    // Backtick-quoted heredoc:
    lines = grammar.tokenizeLines('<<~`EOS`\nThis is text\nEOS');
    expect(lines[0][0]).toEqual({value: '<<~`EOS`', scopes: ['source.ruby', 'string.unquoted.heredoc.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(lines[2][0]).toEqual({value: 'EOS', scopes: ['source.ruby', 'string.unquoted.heredoc.ruby', 'punctuation.definition.string.end.ruby']});
});

  it("tokenizes heredoc which includes identifier in end of a line", function() {
    const lines = grammar.tokenizeLines('<<-EOS\nThis is text\nThis is Not EOS\nEOS');
    expect(lines[0][0]).toEqual({value: '<<-EOS', scopes: ['source.ruby', 'string.unquoted.heredoc.ruby', 'punctuation.definition.string.begin.ruby']});
    expect(lines[3][0]).toEqual({value: 'EOS', scopes: ['source.ruby', 'string.unquoted.heredoc.ruby', 'punctuation.definition.string.end.ruby']});
});

  it("tokenizes Kernel support functions autoload? and exit!", function() {
    const lines = grammar.tokenizeLines('p autoload?(:test)\nexit!\nat_exit!');
    expect(lines[0][2]).toEqual({value: 'autoload?', scopes: ['source.ruby', 'support.function.kernel.ruby']});
    expect(lines[1][0]).toEqual({value: 'exit!', scopes: ['source.ruby', 'support.function.kernel.ruby']});
    expect(lines[2][0]).toEqual({value: 'at_exit!', scopes: ['source.ruby']});
});

  it("tokenizes iterator? the same way as block_given?", function() {
    const lines = grammar.tokenizeLines('p iterator?\np block_given?');
    expect(lines[0][2].value).toEqual('iterator?');
    expect(lines[1][2].value).toEqual('block_given?');
    expect(lines[0][2].scopes).toEqual(lines[1][2].scopes);
  });

  describe("firstLineMatch", function() {
    it("recognises interpreter directives", function() {
      let line;
      const valid = `\
#!/usr/sbin/ruby foo
#!/usr/bin/rake foo=bar/
#!/usr/sbin/jruby
#!/usr/sbin/rbx foo bar baz
#!/usr/bin/rake perl
#!/usr/bin/macruby bin/perl
#!/usr/bin/rbx
#!/bin/rbx
#!/bin/env ruby_executable_hooks
#!/usr/bin/ruby --script=usr/bin
#! /usr/bin/env A=003 B=149 C=150 D=xzd E=base64 F=tar G=gz H=head I=tail rbx
#!\t/usr/bin/env --foo=bar ruby --quu=quux
#! /usr/bin/ruby
#!/usr/bin/env ruby\
`;
      for (line of Array.from(valid.split(/\n/))) {
        expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull();
      }

      const invalid = `\
\x20#!/usr/sbin/ruby
\t#!/usr/sbin/rake
#!/usr/bin/env-ruby/node-env/
#!/usr/bin/env-ruby
#! /usr/binrake
#!\t/usr/bin/env --ruby=bar\
`;
      return (() => {
        const result = [];
        for (line of Array.from(invalid.split(/\n/))) {
          result.push(expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull());
        }
        return result;
      })();
    });

    it("recognises Emacs modelines", function() {
      let line;
      const valid = `\
#-*- Ruby -*-
#-*- mode: Ruby -*-
/* -*-ruby-*- */
// -*- RUBY -*-
/* -*- mode:RUBY -*- */
// -*- font:bar;mode:Ruby -*-
// -*- font:bar;mode:ruby;foo:bar; -*-
// -*-font:mode;mode:Ruby-*-
// -*- foo:bar mode: ruby bar:baz -*-
" -*-foo:bar;mode:ruby;bar:foo-*- ";
" -*-font-mode:foo;mode:ruby;foo-bar:quux-*-"
"-*-font:x;foo:bar; mode : ruby; bar:foo;foooooo:baaaaar;fo:ba;-*-";
"-*- font:x;foo : bar ; mode : RUBY ; bar : foo ; foooooo:baaaaar;fo:ba-*-";\
`;
      for (line of Array.from(valid.split(/\n/))) {
        expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull();
      }

      const invalid = `\
/* --*ruby-*- */
/* -*-- ruby -*-
/* -*- -- RUBY -*-
/* -*- RUBY -;- -*-
// -*- iRUBY -*-
// -*- ruby; -*-
// -*- ruby-stuff -*-
/* -*- model:ruby -*-
/* -*- indent-mode:ruby -*-
// -*- font:mode;Ruby -*-
// -*- mode: -*- Ruby
// -*- mode: i-named-my-dog-ruby -*-
// -*-font:mode;mode:ruby--*-\
`;
      return (() => {
        const result = [];
        for (line of Array.from(invalid.split(/\n/))) {
          result.push(expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull());
        }
        return result;
      })();
    });

    it("recognises Vim modelines", function() {
      let line;
      const valid = `\
vim: se filetype=ruby:
# vim: se ft=ruby:
# vim: set ft=Ruby:
# vim: set filetype=RUBY:
# vim: ft=RUBY
# vim: syntax=Ruby
# vim: se syntax=ruBy:
# ex: syntax=rUBy
# vim:ft=RubY
# vim600: ft=ruby
# vim>600: set ft=ruby:
# vi:noai:sw=3 ts=6 ft=ruby
# vi::::::::::noai:::::::::::: ft=ruby
# vim:ts=4:sts=4:sw=4:noexpandtab:ft=ruby
# vi:: noai : : : : sw   =3 ts   =6 ft  =ruby
# vim: ts=4: pi sts=4: ft=ruby: noexpandtab: sw=4:
# vim: ts=4 sts=4: ft=ruby noexpandtab:
# vim:noexpandtab sts=4 ft=ruby ts=4
# vim:noexpandtab:ft=RUBY
# vim:ts=4:sts=4 ft=ruby:noexpandtab:\x20
# vim:noexpandtab titlestring=hi\|there\\\\ ft=ruby ts=4\
`;
      for (line of Array.from(valid.split(/\n/))) {
        expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull();
      }

      const invalid = `\
ex: se filetype=ruby:
_vi: se filetype=ruby:
 vi: se filetype=ruby
# vim set ft=rubyy
# vim: soft=ruby
# vim: clean-syntax=ruby:
# vim set ft=ruby:
# vim: setft=ruby:
# vim: se ft=ruby backupdir=tmp
# vim: set ft=ruby set cmdheight=1
# vim:noexpandtab sts:4 ft:ruby ts:4
# vim:noexpandtab titlestring=hi\\|there\\ ft=ruby ts=4
# vim:noexpandtab titlestring=hi\\|there\\\\\\ ft=ruby ts=4\
`;
      return (() => {
        const result = [];
        for (line of Array.from(invalid.split(/\n/))) {
          result.push(expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull());
        }
        return result;
      })();
    });
  });
});
